    ; 用户程序
    ; @author: Milon
    ; @date: 2024/04/30

section app_header
    length       dq  app_end                        ; 0x00 用户程序总长度
    entry        dq  start                          ; 0x08 用户程序入口点偏移量
    linear       dq  0                              ; 0x10 用户程序起始线性地址

section app_data
    tid_prex     db "Thread ", 0
    thrd_msg     db " has completed the calculation.", 0
    share_d      dq 0
    mutex_ptr    dq 0                               ; 互斥锁变量的线性地址
    buff    times 256 db 0
 
section app_code
    %include "./common/user_static64.asm"

    bits 64


thread_procedure1:                      ; 线程1过程调用，共享变量加10000000次
        mov rbp, rsp
        sub rsp, 32                     ; 模拟C语言，在栈中开辟32字节用于保存局部变量

        mov rax, 10                     ; 调用10号系统功能，申请160字节内存，内存起始线性地址在R13中
        mov rdx, 160
        syscall

        mov [rbp - 8], r13              ; 保存总字符串缓冲区的线性地址

        add r13, 128
        mov [rbp - 16], r13             ; 用来保存线程标识的文本

        mov rax, 8                      ; 系统调用8号功能获取线程ID
        syscall

        mov r8, rax
        mov rbx, [rbp - 16]
        call bin64_to_dec               ; 将线程标识转换为字符串

        mov rdx, [rel mutex_ptr]
        mov rax, 13                     ; 获取互斥锁
        syscall

        mov rcx, 10000000
    .plus:
        inc qword [rel share_d]
        loop .plus

        mov rax, 14                     ; 释放锁
        syscall


        mov r12, [rel linear]           ; 当前程序加载的起始线性地址

        mov rdi, [rbp-8]                ; 总字符串缓冲区的线性地址
        mov byte [rdi], 0

        lea rsi, [r12 + tid_prex]
        call strcat                     ; 字符串连接

        mov rsi, [rbp-16]
        call strcat

        lea rsi, [r12 + thrd_msg]
        call strcat                     ; 字符串连接

        mov rax, 0                      ; 确定当前线程可以使用的显示行
        syscall                         ; 可用显示行，DH=行号

        mov dl, 0
        mov r9b, 0x0f

        mov rax, 2                      ; 在指定坐标显示字符串
        mov rbx, rdi
        syscall

        mov rsp, rbp
        ret


thread_procedure2:                      ; 线程2过程调用，共享变量减10000000次
        mov rbp, rsp
        sub rsp, 32

        mov rax, 10                     ; 调用10号系统功能，申请160字节内存，内存起始线性地址在R13中
        mov rdx, 160
        syscall

        mov [rbp - 8], r13              ; 保存总字符串缓冲区的线性地址

        add r13, 128
        mov [rbp - 16], r13             ; 用来保存线程标识的文本

        mov rax, 8                      ; 系统调用8号功能获取线程ID
        syscall

        mov r8, rax
        mov rbx, [rbp - 16]
        call bin64_to_dec               ; 将线程标识转换为字符串

        mov rdx, [rel mutex_ptr]
        mov rax, 13                     ; 获取互斥锁
        syscall

        mov rcx, 10000000
    .minus:
        dec qword [rel share_d]
        loop .minus

        mov rax, 14                     ; 释放互斥锁
        syscall

        mov r12, [rel linear]           ; 当前程序加载的起始线性地址

        mov rdi, [rbp-8]                ; 总字符串缓冲区的线性地址
        mov byte [rdi], 0

        lea rsi, [r12 + tid_prex]
        call strcat                     ; 字符串连接

        mov rsi, [rbp-16]
        call strcat

        lea rsi, [r12 + thrd_msg]
        call strcat                     ; 字符串连接

        mov rax, 0                      ; 确定当前线程可以使用的显示行
        syscall                         ; 可用显示行，DH=行号

        mov dl, 0
        mov r9b, 0x0f

        mov rax, 2                      ; 在指定坐标显示字符串
        mov rbx, rdi
        syscall

        mov rsp, rbp
        ret



main:
    mov rbp, rsp
    sub rsp, 16                         ; 在栈中开辟2个4字，用于保存两个线程ID作为局部变量

    mov r12, [rel linear]

    ; 初始化锁
    mov rax, 12
    syscall
    mov [rel mutex_ptr], rdx
    
    mov rax, 7
    lea rsi, [r12 + thread_procedure1]  ; 创建线程1
    syscall
    mov qword [rbp - 8], rdx            ; 栈中保存线程1的ID

    lea rsi, [r12 + thread_procedure2]  ; 创建线程2
    syscall
    mov qword [rbp - 16], rdx           ; 栈中保存线程2的ID

    mov rax, 11
    mov rdx, [rbp - 8]                  ; 调用11号系统功能，等待线程1结束
    syscall
    mov rdx, [rbp - 16]                 ; 调用11号系统功能，等待线程2结束
    syscall

    ; 连接两个字符串
    lea rdi, [r12 + .main_buf]
    mov byte [rdi], 0
    lea rsi, [r12 + .main_msg]
    call strcat

    ; share_d变量转换为十进制ASCII字符
    mov r8, [rel share_d]
    lea rbx, [r12 + .main_dat]
    call bin64_to_dec

    mov rsi, rbx
    call strcat

    mov rax, 0
    syscall                             ; dh=行号
    mov dl, 0
    mov r9b, 0x0f                       ; 颜色属性
    mov rbx, rdi
    mov rax, 2
    syscall

    mov rsp, rbp
    ret

.main_msg   db      "The result after calculation by two threads is:", 0
.main_dat   times   32  db 0
.main_buf   times   128 db 0

start:   
    call main

    mov rax, 5              ; 系统调用5号功能，终止当前进程
    syscall
    
app_end:
